package com.web3.management.service;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.web3.management.dto.request.CommandAckRequest;
import com.web3.management.dto.response.CommandDispatchResponse;
import com.web3.management.dto.tm.QrCapture;
import com.web3.management.dto.tm.SessionCapture;
import com.web3.management.entity.Account;
import com.web3.management.entity.TmClient;
import com.web3.management.entity.TmCommand;
import com.web3.management.exception.BusinessException;
import com.web3.management.repository.AccountRepository;
import com.web3.management.repository.TmCommandRepository;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.stream.Collectors;

/**
 * Tampermonkey 指令调度服务
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class TmCommandService {

    private final TmCommandRepository tmCommandRepository;
    private final TmClientService tmClientService;
    private final AccountCredentialService accountCredentialService;
    private final AccountRepository accountRepository;
    private final ObjectMapper objectMapper;

    @Transactional
    public TmCommand createCommand(Integer accountId, String commandType, String payload) {
        if (!StringUtils.hasText(commandType)) {
            throw new BusinessException("指令类型不能为空");
        }
        TmClient client = tmClientService.findByAccountId(accountId)
                .orElseThrow(() -> new BusinessException("账号未绑定客户端"));
        Account account = accountRepository.findById(accountId)
                .orElseThrow(() -> new BusinessException("账号不存在"));

        TmCommand command = new TmCommand();
        command.setAccountId(accountId);
        command.setClientId(client.getClientId());
        command.setCommandType(commandType.toUpperCase(Locale.ROOT));
        command.setPayload(payload);
        command.setStatus("PENDING");

        TmCommand saved = tmCommandRepository.save(command);
        client.setLastCommandAt(new Date());
        tmClientService.save(client);

        log.info("创建指令: accountId={}, accountName={}, clientId={}, type={}",
                accountId, account.getName(), client.getClientId(), commandType);
        return saved;
    }

    @Transactional(readOnly = true)
    public long countPendingByAccount(Integer accountId) {
        return tmCommandRepository.countByAccountIdAndStatus(accountId, "PENDING");
    }

    @Transactional
    public List<CommandDispatchResponse> pollCommands(String clientId) {
        TmClient client = tmClientService.ensureRegistered(clientId);
        List<TmCommand> pending = tmCommandRepository.findTop20ByClientIdAndStatusOrderByCreatedAtAsc(client.getClientId(), "PENDING");
        Date now = new Date();
        for (TmCommand command : pending) {
            command.setStatus("SENT");
            command.setSentAt(now);
            tmCommandRepository.save(command);
        }
        client.setLastSeenAt(now);
        client.setLastCommandAt(now);
        tmClientService.save(client);

        return pending.stream()
                .map(this::toDispatchResponse)
                .collect(Collectors.toList());
    }

    @Transactional
    public void handleAck(Integer commandId, String clientId, CommandAckRequest request) {
        TmCommand command = tmCommandRepository.findById(commandId)
                .orElseThrow(() -> new BusinessException("指令不存在"));
        if (!command.getClientId().equals(clientId)) {
            throw new BusinessException("客户端与指令不匹配");
        }
        Date now = new Date();
        command.setAckAt(now);
        command.setStatus("ACKED");
        command.setResultStatus(request.getStatus());
        command.setMessage(request.getMessage());
        String payloadRaw = null;
        if (request.getPayload() != null) {
            try {
                payloadRaw = objectMapper.writeValueAsString(request.getPayload());
            } catch (JsonProcessingException e) {
                log.error("序列化指令 payload 失败", e);
                throw new BusinessException("指令payload格式错误");
            }
        }
        command.setResultPayload(payloadRaw);
        tmCommandRepository.save(command);

        tmClientService.findByClientId(clientId).ifPresent(client -> {
            client.setLastSeenAt(now);
            client.setLastCommandAt(now);
            tmClientService.save(client);
        });

        if (!StringUtils.hasText(command.getCommandType())) {
            return;
        }
        String type = command.getCommandType().toUpperCase(Locale.ROOT);
        try {
            if ("REQUEST_SESSION".equals(type)) {
                SessionCapture capture = request.getPayload() == null ? null : objectMapper.treeToValue(request.getPayload(), SessionCapture.class);
                accountCredentialService.handleSessionCapture(command.getAccountId(), clientId, payloadRaw, capture,
                        request.getStatus(), request.getMessage());
            } else if ("REQUEST_QR".equals(type)) {
                QrCapture capture = request.getPayload() == null ? null : objectMapper.treeToValue(request.getPayload(), QrCapture.class);
                accountCredentialService.handleQrCapture(command.getAccountId(), clientId, payloadRaw, capture,
                        request.getStatus(), request.getMessage());
            }
        } catch (Exception e) {
            log.error("处理指令结果失败: commandId={}, type={}", commandId, type, e);
            throw new BusinessException("处理指令结果失败");
        }
    }

    private CommandDispatchResponse toDispatchResponse(TmCommand command) {
        JsonNode payloadNode = null;
        if (StringUtils.hasText(command.getPayload())) {
            try {
                payloadNode = objectMapper.readTree(command.getPayload());
            } catch (Exception e) {
                log.warn("解析指令 payload 失败: id={}", command.getId(), e);
            }
        }
        return new CommandDispatchResponse(command.getId(), command.getCommandType(), payloadNode, command.getCreatedAt());
    }

}
